package com.eden.door.util;

import java.text.NumberFormat;
import java.text.ParseException;
import java.util.ArrayDeque;
import java.util.Deque;
import java.util.Locale;

import org.apache.commons.lang.ArrayUtils;

/**
 * 处理四则混合运算
 * @author Eden
 *
 */
public class Calculator_Stack {
	public static Calculator calculator = new Calculator() ;
	
	private String expressionStr = null ;
	
	private Deque<String> numDeque ;
	private Deque<Character> signDeque ;
	
	private String splitStr = "\\^" ;
	
	private NumberFormat nf = null ;
	
	char[] sign = {'+' , '-' , '*' , '/' , '(' , ')' , '#'} ;
	
	char[][] ss = {
			//+     -     *    /    (    )    #
	/*+*/   {'>' , '>' , '<', '<', '<', '>', '>'} ,
	/*-*/	{'>' , '>' , '<', '<', '<', '>', '>'} ,
	/***/	{'>' , '>' , '>', '>', '<', '>', '>'} ,
	/*/*/	{'>' , '>' , '>', '>', '<', '>', '>'} ,
	/*(*/	{'<' , '<' , '<', '<', '<', '=',  0 } ,
	/*)*/	{'>' , '>' , '>', '>',  0 , '>', '>'} ,
	/*#*/	{'<' , '<' , '<', '<', '<', 0  , '='} 
	} ;
	
	
	/**
	 * 构造方法 ，初始化参数
	 */
	public Calculator_Stack(){
		this.numDeque = new ArrayDeque<String>() ;
		this.signDeque = new ArrayDeque<Character>() ;
		this.nf = NumberFormat.getInstance(Locale.CHINA) ;
		nf.setMinimumFractionDigits(2) ;
		nf.setMaximumFractionDigits(4) ;
	} 
	
	public Calculator_Stack(String expressionStr){
		this.expressionStr = expressionStr ;
	}
	
	public static Calculator newInstance(){
		return calculator ;
	}
	
	/**
	 * 分解运算表达式
	 * @param expressionStr
	 * @param splitStr
	 * @return
	 */
	public String[] handleExpression(String expressionStr , String splitStr){
		if(expressionStr == null) 
			return null ;
		
		return expressionStr.split(splitStr) ;
	}
	
	/**
	 * 计算运算式
	 * @param exp 运算式字符串
	 * @return 返回计算结果
	 */
	@SuppressWarnings("unused")
	public double calculate(String exp){
		String[] exps = handleExpression(exp, splitStr) ;
		if(exps.length == 3)
			try {
				return nf.parse(exps[1].trim()).doubleValue() ;
			} catch (ParseException e) {
				e.printStackTrace();
			}
		
		//循环处理数据
		n:for(String s : exps ){
			s = s.trim() ;
			System.out.println(s) ;
			if(ArrayUtils.contains(sign, s.charAt(0))){
				if(signComp(s.charAt(0)) == -1 ){
					numDeque.push(nf.format(compute()) );
					if(s.charAt(0) == ')'){
						signDeque.pop() ;
					} else
						signDeque.push(s.charAt(0)) ;
				} else if(signComp(s.charAt(0)) == 0) {
					numDeque.push(nf.format(compute()) );
					signDeque.pop() ;
				} else if(signComp(s.charAt(0)) == 1 ){
					signDeque.push(s.charAt(0)) ;
				}
				
			} else {
				numDeque.push(s) ;
			}
		
		}
		
		String expDb = getExp() ;
		return calculate(expDb) ;
	}
	
	protected String getExp(){
		Deque<Character> signTemp = new ArrayDeque<Character>() ;
		Deque<String> numTemp = new ArrayDeque<String>() ;
		
		while(signDeque.peek() != null) signTemp.push(signDeque.pop()) ;
		while(numDeque.peek() != null) numTemp.push(numDeque.pop()) ;
		
		StringBuilder exp = new StringBuilder("") ;
			while(signTemp.peek() != null){
				exp.append(signTemp.poll()) ;
				if(numTemp.peek() ==null) break ;
				exp.append("^") ;
				exp.append(numTemp.poll()) ;
				exp.append("^") ;
			}
		return exp.toString() ;
	}
	protected double lastCompu(){
		return 0 ;
	}
	/**
	 * 从栈中取值计算
	 * @return
	 */
	protected double compute(){
		try {
			double num1 = Double.parseDouble(numDeque.pop()) ;
			double num2 = Double.parseDouble(numDeque.pop()) ;
			
			Character sign = signDeque.pop() ;
			
			switch (sign){
			case '+' : return num2 + num1 ; 
			case '-' : return num2 - num1 ;
			case '*' : return num2 * num1 ;
			case '/' : try {
					return num2 / num1 ;
				} catch (Exception e) {
					e.printStackTrace();
					throw new IllegalArgumentException("除数不能为零") ;
				}
			default :
				throw new RuntimeException("运算符号不正确") ;
			}
		} catch (NumberFormatException e) {
			e.printStackTrace();
			throw new IllegalArgumentException("运算表达式错误!") ;
		}
	}
	
	/**
	 * 运算符号优先级比较 , 当前的运算符与之前的一个比较
	 * @param currSign 当前的运算符号
	 * @return 高与之前的返回 1 , 等于返回0 , 小于返回-1
	 */
	protected int signComp(Character currSign){
		Character preSign = signDeque.peek() ;
		
		//如果操作符栈中没有数 据将当前操作符压入栈
		if(preSign == null){
//			signDeque.push(currSign) ;
			return 1 ;
		} else {
			if(ss[convertSign2Num(preSign)][convertSign2Num(currSign)] == '>'){
				return -1 ;
			} else if(ss[convertSign2Num(preSign)][convertSign2Num(currSign)] == '<'){
				return 1 ;
			} else if(ss[convertSign2Num(preSign)][convertSign2Num(currSign)] == '='){
				return 0 ;
			}
		}
		return 2;
	}
	
	/**
	 * 转换运算符号为数字
	 * @param sign
	 * @return
	 */
	protected int convertSign2Num(Character sign){
		switch (sign) {
		case '+':
			return 0 ;
		case '-':
			return 1 ;
		case '*':
			return 2 ;
		case '/':
			return 3 ;
		case '(':
			return 4 ;
		case ')':
			return 5 ;
		case '#':
			return 6 ;

		default:
			return -1;
		}
	}
	
	public static void main(String[] args){
	}
}
